/*
 *  YATUN  - Yet another tunneling application
 *
 *  Michal Demin - 2007
 *
 *  IO_Tun.cpp
 *  TUN/TAP komunikacny modul
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/if.h>
#include <linux/if_tun.h>
#include <errno.h>

#include "IO_Tun.h"

IO_Tun::IO_Tun() {
    const char * name_loc = "IO_Tun";
    
    memset(&Name, 0, NAME_LENGHT);
    memcpy(&Name,name_loc, 6);
    Flag = PLUG_FLAG_IO;
    If_name = strdup("yatun%d");
}

IO_Tun::IO_Tun(char * Ifname) {
    const char * name_loc = "IO_Tun";
    
    memset(&Name, 0, NAME_LENGHT);
    memcpy(&Name,name_loc, 6);

    Flag = PLUG_FLAG_IO;
    If_name = strdup(Ifname);
}

IO_Tun::~IO_Tun() {
    if(fd) 
	Close();
    free(If_name);
}

int IO_Tun::Open() {
    struct ifreq ifr;

    fd = open("/dev/net/tun", O_RDWR);
    if (fd < 0) {
	printf("Forgot to  load \"tun\" module ? \n Maybe you don't have permision to access tun ! \n");
	return -1;
    }

    memset(&ifr, 0, sizeof(ifr));

    ifr.ifr_flags = IFF_TAP | IFF_NO_PI;

    if (*If_name)
	strncpy(ifr.ifr_name, If_name, IFNAMSIZ);

    if (ioctl(fd, TUNSETIFF, (void *) &ifr) < 0) {
	goto end;
    }

    strcpy(If_name, ifr.ifr_name);

/* bring up interface  
    if(ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) {
	printf("Can't read interface flags \n");
	goto end;
    }
    ifr.ifr_flags |= IFF_UP;
    if(ioctl(fd, SIOCSIFFLAGS, &ifr) < 0) {
	printf("Can't writeinterface flags \n");
	goto end;
    }
*/
    return 0;

end:
    close(fd);
    return -1;

}

int IO_Tun::Connect() {
    return 0;
}

int IO_Tun::Close() {
    close(fd);
    fd = 0;
    return 0;
}

int IO_Tun::Recieve(Packet * Pack) {
    int ret;

    ret = read(fd, (void *)Pack->Data, sizeof(Pack->Data));

    Pack->Flag = YATUN_PACKET_DATA;
    Pack->Lenght = ret;

    return ret;
}

int IO_Tun::Send(Packet * Pack) {
    return write(fd, (void *)Pack->Data, Pack->Lenght);
}

int IO_Tun::DataReady() {
    struct timeval timeout;
    fd_set rfds;
    int ret;
    
    timeout.tv_sec = 1;
    timeout.tv_usec = 20;
    
    FD_ZERO(&rfds);
    FD_SET(fd, &rfds);
    
    ret = select(fd+1, &rfds, NULL, NULL, &timeout);
    
    if (ret == -1) {
        return 0;
    } else if (ret) {
//        fprintf(stderr,"Data is available now.\n");
        if(FD_ISSET(fd, &rfds)) {
            return 1;
        }
    }
    
    return 0;
}

int IO_Tun::GetFD() {
    return fd;
}

